/*
 * Copyright (c) 2007 Thomas Fors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// =======================================================
// 3x3 Matrix Routines
// =======================================================


// -------------------------------------------------------
// Matrix3x3()
// Constructor
// -------------------------------------------------------
function Matrix3x3(a11, a12, a13, a21, a22, a23, a31, a32, a33) {
	if ( (a11 instanceof Vector1x3) && (a12 instanceof Vector1x3) && (a13 instanceof Vector1x3) ) {
		// Three vectors have been specified.  Constuct matrix with each vector as a row
		this.a11 = a11.a11;
		this.a12 = a11.a12;
		this.a13 = a11.a13;
		this.a21 = a12.a11;
		this.a22 = a12.a12;
		this.a23 = a12.a13;
		this.a31 = a13.a11;
		this.a32 = a13.a12;
		this.a33 = a13.a13;
	} else {
		// assume individual matrix elements have been specified and contruct it accordingly
		this.a11 = a11;
		this.a12 = a12;
		this.a13 = a13;
		this.a21 = a21;
		this.a22 = a22;
		this.a23 = a23;
		this.a31 = a31;
		this.a32 = a32;
		this.a33 = a33;
	}
}

// -------------------------------------------------------
// SetBradford()
// Sets matrix to Bradford Chromatic Adapatation Matrix
// -------------------------------------------------------
Matrix3x3.prototype['SetBradford'] = function () {
	this.a11 =  0.8951;		this.a12 = -0.7502;		this.a13 =  0.0389;
	this.a21 =  0.2664;		this.a22 =  1.7135;		this.a23 = -0.0685;
	this.a31 = -0.1614;		this.a32 =  0.0368;		this.a33 =  1.0296;
}

// -------------------------------------------------------
// Inverse()
// Returns inverse of matrix.  Does not alter this matrix.
// -------------------------------------------------------
Matrix3x3.prototype['Inverse'] = function () {
	var det = this.Determinant();
	if ( det == 0 )
		throw("Attempted inverse of singular matrix.");
	
	var inv = new Matrix3x3( 	this.det2x2(this.a22,this.a23,this.a32,this.a33)/det,
								this.det2x2(this.a13,this.a12,this.a33,this.a32)/det,
								this.det2x2(this.a12,this.a13,this.a22,this.a23)/det,
								this.det2x2(this.a23,this.a21,this.a33,this.a31)/det,
								this.det2x2(this.a11,this.a13,this.a31,this.a33)/det,
								this.det2x2(this.a13,this.a11,this.a23,this.a21)/det,
								this.det2x2(this.a21,this.a22,this.a31,this.a32)/det,
								this.det2x2(this.a12,this.a11,this.a32,this.a31)/det,
								this.det2x2(this.a11,this.a12,this.a21,this.a22)/det  );
	return inv;
}

// -------------------------------------------------------
// Determinant()
// Returns determinant of matrix.
// -------------------------------------------------------
Matrix3x3.prototype['Determinant'] = function () {
	var det =  this.a11*this.a22*this.a33;
	    det -= this.a11*this.a23*this.a32;
	    det -= this.a12*this.a21*this.a33;
	    det += this.a12*this.a23*this.a31;
	    det += this.a13*this.a21*this.a32;
	    det -= this.a13*this.a22*this.a31;
				
	return det;
}


// -------------------------------------------------------
// det2x2()
// Utility function to compute determinant of 2x2 matrix
// -------------------------------------------------------
Matrix3x3.prototype['det2x2'] = function (a11, a12, a21, a22) {
	return ( (a11 * a22) - (a12 * a21) );
}

// -------------------------------------------------------
// Multiply()
// Multiplies lhs (left hand side) and matrix together.
// lhs can be another 3x3 matrix, a 1x3 vector, or a scalar.
// Returns appropriate result.  This matrix is not changed.
// -------------------------------------------------------
Matrix3x3.prototype['Multiply'] = function (lhs) {
	if ( lhs instanceof Matrix3x3 ) {
		// Matrix * Matrix
		return new Matrix3x3(  lhs.a11*this.a11 + lhs.a12*this.a21 + lhs.a13*this.a31,
							   lhs.a11*this.a12 + lhs.a12*this.a22 + lhs.a13*this.a32,
							   lhs.a11*this.a13 + lhs.a12*this.a23 + lhs.a13*this.a33,
							   lhs.a21*this.a11 + lhs.a22*this.a21 + lhs.a23*this.a31,
							   lhs.a21*this.a12 + lhs.a22*this.a22 + lhs.a23*this.a32,
							   lhs.a21*this.a13 + lhs.a22*this.a23 + lhs.a23*this.a33,
							   lhs.a31*this.a11 + lhs.a32*this.a21 + lhs.a33*this.a31,
							   lhs.a31*this.a12 + lhs.a32*this.a22 + lhs.a33*this.a32,
							   lhs.a31*this.a13 + lhs.a32*this.a23 + lhs.a33*this.a33   );
	}
	else if ( lhs instanceof Vector1x3 ) {
		// Vector * Matrix
		return new Vector1x3(  lhs.a11*this.a11 + lhs.a12*this.a21 + lhs.a13*this.a31,
							   lhs.a11*this.a12 + lhs.a12*this.a22 + lhs.a13*this.a32,
							   lhs.a11*this.a13 + lhs.a12*this.a23 + lhs.a13*this.a33   );
	}
	else {
		// Scalar * Matrix
		return new Matrix3x3(  lhs*this.a11, lhs*this.a12, lhs*this.a13,
							   lhs*this.a21, lhs*this.a22, lhs*this.a23,
							   lhs*this.a31, lhs*this.a32, lhs*this.a33   );
	}
}

// -------------------------------------------------------
// Display()
// Uses an alert box to display matrix for debugging purposes
// -------------------------------------------------------
Matrix3x3.prototype['Display'] = function () {
	var msg = "";
	msg += Math.round(this.a11*1000000)/1000000 + "\t";
	msg += Math.round(this.a12*1000000)/1000000 + "\t";
	msg += Math.round(this.a13*1000000)/1000000 + "\n";
	
	msg += Math.round(this.a21*1000000)/1000000 + "\t";
	msg += Math.round(this.a22*1000000)/1000000 + "\t";
	msg += Math.round(this.a23*1000000)/1000000 + "\n";
	
	msg += Math.round(this.a31*1000000)/1000000 + "\t";
	msg += Math.round(this.a32*1000000)/1000000 + "\t";
	msg += Math.round(this.a33*1000000)/1000000 + "\n";
	
	alert(msg);
}
